/**
 * HOMER Core - an open and flexible OSGi-based software platform for SmartHome and AAL applications
 *
 * Copyright (C) 2011-2013 AIT Austrian Institute of Technology GmbH - <http://www.ait.ac.at>
 * Contact: Thomas Fuxreiter <thomas.fuxreiter@ait.ac.at>
 *
 * This file is part of HOMER Core.
 *
 * HOMER Core is free software; you can redistribute it and/or
 * modify it under the terms of the GNU Lesser General Public
 * License as published by the Free Software Foundation; either
 * version 2.1 of the License, or (at your option) any later version.
 *
 * HOMER Core is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
 * Lesser General Public License for more details.
 *
 * You should have received a copy of the GNU Lesser General Public
 * License along with this program. If not, see <http://www.gnu.org/licenses/>.
 */
package at.aal2aac.eventbusclient;

import java.net.URI;
import java.net.URISyntaxException;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.UUID;

import org.apache.log4j.Logger;

import at.aal2aac.eventbusclient.EventbusMessage;
import at.aal2aac.eventbusclient.JsonUtils;
import at.ac.ait.hbs.homer.core.common.enumerations.DeviceMessageType;
import at.ac.ait.hbs.homer.core.common.homecontrol.HomeControlCommand;
import at.ac.ait.hbs.homer.core.common.homecontrol.enumerations.Command;
import de.roderick.weberknecht.WebSocketConnection;
import de.roderick.weberknecht.WebSocketEventHandler;
import de.roderick.weberknecht.WebSocketException;
import de.roderick.weberknecht.WebSocketMessage;

/**
 * A java implementation of a client for a remote connection to the HOMER eventbus over a websocket connection
 * 
 * @author kropfj
 *
 */
public class RemoteEventbusClient {


	private Map<String, List<Handler>> msgHandlers = new HashMap<String, List<Handler>>();
	private Map<String, List<Handler>> replyHandlers = new HashMap<String, List<Handler>>();

	private WebSocketConnection websocket;
	private Status status = Status.CLOSED;
	private String id = "demoClient";

	enum Status {
		OPEN, CLOSED, CLOSING, CONNECTING
	};

	public void init() {
	
		System.out.println("Initializing client");

		try {
			URI url = new URI("ws://127.0.0.1:1234/eventbus");
			websocket = new WebSocketConnection(url);

			// Register Event Handlers
			websocket.setEventHandler(new WebSocketEventHandler() {

				@Override
				public void onClose() {
					status = Status.CLOSED;
					System.out.println("closing");

				}

				@Override
				public void onMessage(WebSocketMessage arg0) {
					System.out.println("reveived message: " + arg0.getText());
					EventbusMessage msg = JsonUtils.toEventbusMessage(arg0.getText());
					ReplyHandler rh;

					if (msg.getReplyAddress() != null)
						rh = new ReplyHandler();

					List<Handler> list = msgHandlers.get(msg.getAddress());

					// might be a reply message
					if (list == null || list.isEmpty()) {
						list = replyHandlers.get(msg.getAddress());
					} else {

					}

				}

				@Override
				public void onOpen() {
					System.out.println("open");

				}

			});

			websocket.connect();
			System.out.println("connected");

		} catch (WebSocketException wse) {
			System.out.println("Error: "+ wse);
		} catch (URISyntaxException use) {
			System.out.println("Error: "+ use);
		}

		//sendOrPub("connect", "hello", "connect", null);

		registerHandler(this.id, new Handler() {

			@Override
			public void handleMessage(String msg, String replyTo) {
				System.out.println("received: " + msg);

			}
		});
	
		//sendOrPub("publish", "hello", "Hello from remote client", new ReplyHandler());
        //sendOrPub("register", this.id, this.id, new ReplyHandler());
		
		
		Command c = Command.REQUEST_FLAT_CONFIG;
		HomeControlCommand hcm = new HomeControlCommand(c);
		//sendOrPub1("send", "homecontrol", hcm, new ReplyHandler());
		c = Command.SWITCH_ACTUATOR;
		DeviceMessageType dmt = DeviceMessageType.MDC_AI_FLAG_SENSOR_CONTACTCLOSURE_CLOSED;
		HomeControlCommand hcm1 = new HomeControlCommand(c, 2, dmt);
		sendOrPub1("send","homecontrol",hcm1,new ReplyHandler());
		
		
	}

	public void send(final String address, final String message, final ReplyHandler replyHandler) {
		sendOrPub("send", address, message, replyHandler);
	}

	public void publish(final String address, final String message, final ReplyHandler replyHandler) {
		sendOrPub("publish", address, message, replyHandler);
	}

	public void sendOrPub(final String sendOrPub, final String address, final String message, final ReplyHandler replyHandler) {
		EventbusMessage<String> msg = new EventbusMessage<String>();
		msg.setAddress(address);

		if (sendOrPub.equalsIgnoreCase("send")) {
			msg.setMode(EventbusMessage.SEND);
		} else if (sendOrPub.equalsIgnoreCase("publish")){
			msg.setMode(EventbusMessage.PUBLISH);
		} else if (sendOrPub.equalsIgnoreCase("register")) {
			msg.setMode(EventbusMessage.REGISTER);
		}

		msg.setBody(message);

		if (replyHandler != null) {
			msg.setReplyAddress(this.id);
			System.out.println("Address: " + msg.getAddress());
		}

		sendToBus(JsonUtils.toJsonString(msg));

	}
	
	public void sendOrPub1(final String sendOrPub, final String address, final HomeControlCommand message, final ReplyHandler replyHandler) {
		EventbusMessage<HomeControlCommand> msg = new EventbusMessage<HomeControlCommand>();
		msg.setAddress(address);

		if (sendOrPub.equalsIgnoreCase("send")) {
			msg.setMode(EventbusMessage.SEND);
		} else if (sendOrPub.equalsIgnoreCase("publish")){
			msg.setMode(EventbusMessage.PUBLISH);
		} else if (sendOrPub.equalsIgnoreCase("register")) {
			msg.setMode(EventbusMessage.REGISTER);
		}

		msg.setBody(message);

		if (replyHandler != null) {
			msg.setReplyAddress(this.id);
			System.out.println("Address: " + msg.getReplyAddress());
		}

		sendToBus(JsonUtils.toJsonString(msg));
	}
	  
	public void destroy() {
		System.out.println("unregister all handlers");

		for (String address : msgHandlers.keySet()) {
			sendToBus(JsonUtils.toJsonString(new EventbusMessage<String>(id, address, EventbusMessage.UNREGISTER)));

		}
		System.out.println("stopping client");
		try {
			websocket.close();
		} catch (WebSocketException e) {
			// TODO Auto-generated catch block
			System.out.println("Error: "+ e);
		}
	}

	public void registerHandler(final String address, final Handler h) {
		List<Handler> list = msgHandlers.get(address);

		// if its the first handler for this address, register
		if (list == null || list.isEmpty()) {
			EventbusMessage<String> msg = new EventbusMessage<String>();
			msg.setMode(EventbusMessage.REGISTER);
            msg.setAddress(this.id);
            msg.setReplyAddress(this.id);
			sendToBus(JsonUtils.toJsonString(msg));
			list = new ArrayList<Handler>();
		}

		list.add(h);
		msgHandlers.put(address, list);

	}

	public void unregisterHandler(final String address, final Handler h) {

		List<Handler> list = msgHandlers.get(address);

		// nothing to do
		if (list == null) {
			return;
		}

		// if only one handler left we should unregister the connection
		if (list.size() == 1 && list.contains(h)) {
			sendToBus(JsonUtils.toJsonString(new EventbusMessage<String>(id, address, EventbusMessage.UNREGISTER)));
		}

		list.remove(h);

		// remove also from handlers map
		if (list.isEmpty()) {
			msgHandlers.remove(address);
		}
	}

	public class ReplyHandler implements Handler {

		private String replyAddress;

        public ReplyHandler() {

        }

        public ReplyHandler(String replayAddress) {
            this.setReplyAddress(replayAddress);
        }

		public void setReplyAddress(final String replyAddress) {
			this.replyAddress = replyAddress;
			List<Handler> list = replyHandlers.get(replyAddress);
			
			if (list == null || list.isEmpty()) {
				list = new ArrayList<Handler>();
			}
			
			list.add(this);
			replyHandlers.put(replyAddress, list);
		}

		// send a reply
		public void reply(final String reply) {
			send(replyAddress, reply, this);
		}

		@Override
		public void handleMessage(final String msg, final String replyTo) {

		}

	}

	private void sendToBus(final String msg) {
		try {
			System.out.println("Message to be send: " + msg);
			websocket.send(msg);
		} catch (WebSocketException ex) {
			System.out.println("Error: "+ ex);
		}
	}
	
	public static void main(String [] args)
	{
		RemoteEventbusClient rmClient = new RemoteEventbusClient();
		rmClient.init();
		
	}

}
